home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / ctask.zip / TSKMAIN.C < prev    next >
C/C++ Source or Header  |  1988-07-01  |  17KB  |  772 lines

  1. /*
  2.    TSKMAIN.C - CTask - Main routines for task handling.
  3.  
  4.    CTask - a Multitasking Kernel for C
  5.  
  6.    Public Domain Software written by
  7.       Thomas Wagner
  8.       Patschkauer Weg 31
  9.       D-1000 Berlin 33
  10.       West Germany
  11.  
  12.    No rights reserved.
  13.  
  14.    Version 1.1  88-07-01
  15.  
  16.       Changes from V0.1: - Separated timeout handling from INT9 chaining
  17.                            for better control over TSR response.
  18.                          - Handling of timeout queue rewritten.
  19.                          - More features for timeouts (can handle
  20.                            flags, counters, function calls, and repetititve
  21.                            timeouts).
  22.                          - Support for dynamically allocated control blocks.
  23.                          - Idle task removed.
  24.  
  25. */
  26.  
  27. #include <stdio.h>
  28.  
  29. #include "tsk.h"
  30. #include "tsklocal.h"
  31.  
  32. #define STACKSIZE 512
  33.  
  34. /*
  35.    The task queues:
  36.       All tasks using a timeout, either through "t_delay" or an event wait,
  37.       are enqueued into the "tsk_timer" queue, using the "timerq" link.
  38.       All tasks eligible for running are enqueued in "tsk_eligible".
  39.       The tcb-address of the current running task is stored in "tsk_current".
  40. */
  41.  
  42. tlinkptr _Near tsk_timer;
  43. tcbptr   _Near tsk_eligible;
  44. tcbptr   _Near tsk_current;
  45.  
  46. /*
  47.    System flags:
  48.       tsk_preempt is zero if preemption is allowed.
  49.                   Bit 0 is set if preemption has been disabled globally.
  50.                   Bit 1 is set for temporary disabling preemption.
  51.                   Temporary preemption is automatically removed by the
  52.                   scheduler.
  53.  
  54.       tsk_pretick is nonzero if a schedule request from an interrupt handler
  55.                   was rejected due to tsk_preempt nonzero. This allows
  56.                   an immediate scheduling whenever tsk_preempt is set to 0.
  57.  
  58.       tsk_var_prior Can be set nonzero to enable variable priority.
  59.                     Variable priority will increase the priority of
  60.                     eligible tasks on each scheduler call while they 
  61.                     are waiting to be executed, so that low priority 
  62.                     tasks will slowly get to the head of the eligible
  63.                     queue, getting a chance to be run. With variable
  64.                     priority off, lower priority tasks will never be
  65.                     executed while higher priority tasks are eligible.
  66.                     
  67. */
  68.  
  69. byte _Near tsk_preempt;
  70. byte _Near tsk_pretick;
  71. byte _Near tsk_var_prior;
  72.  
  73. /* --------------------------------------------------------------------- */
  74.  
  75. /*
  76.    The tcb's of the standard tasks.
  77.  
  78.       timer_tcb   is the tcb for the timer task.
  79.                   This task waits for the tsk_timer_counter, which is
  80.                   increased on every timer tick. It processes the entries
  81.                   in the timeout queue.
  82.  
  83.       int9_tcb    is the tcb for the int9 chain task.
  84.                   This task waits for the tsk_int9_counter, which is
  85.                   increased on every system timer tick. It then chains to
  86.                   the previous timer interrupt entry.
  87.  
  88.       main_tcb    is the "main" task which called "install_tasker". This
  89.                   task has no separate stack, rather the stack on entry
  90.                   to the scheduler is used.
  91.  
  92. */
  93.  
  94. counter  _Near tsk_timer_counter;
  95.  
  96. local tcb timer_tcb;
  97. local tcb main_tcb;
  98.  
  99. local char timer_stack [STACKSIZE];
  100.  
  101. #if (IBM)
  102. counter  _Near tsk_int9_counter;
  103. local tcb int9_tcb;
  104. local char int9_stack [STACKSIZE];
  105. #endif
  106.  
  107. #if (CLOCK_MSEC)
  108. double tick_factor;
  109. #endif
  110.  
  111. word _Near ticks_per_sec;
  112.  
  113. #if (TSK_NAMED)
  114. namerec tsk_name_list;
  115. #endif
  116.  
  117.  
  118. /*
  119.    Un-Install-Function pointers for the optional serial and printer 
  120.    drivers. If ports are installed, the driver inserts the address
  121.    of a remove-function here, to be called on removal of the main
  122.    tasker.
  123. */
  124.  
  125. funcptr v24_remove_func = NULL;
  126. funcptr prt_remove_func = NULL;
  127.  
  128.  
  129. /* --------------------------------------------------------------------- */
  130.  
  131. #pragma check_stack(off)
  132.  
  133. /*
  134.    Killretn kills the current active task. It is used internally, but
  135.    can also be called from outside.
  136. */
  137.  
  138. void far killretn (void)
  139. {
  140.    tsk_cli ();
  141.    tsk_kill (tsk_current);
  142.    tsk_current = NULL;
  143.    schedule ();
  144. }
  145.  
  146. /*
  147.    tsk_timer_action performs the necessary action when a timeout occurred.
  148. */
  149.  
  150. local void tsk_timer_action (tlinkptr elem)
  151. {
  152.    tcbptr task;
  153.    byte st;
  154.  
  155.    switch (elem->tkind & 0x7f)
  156.       {
  157.       case TKIND_WAKE:
  158.       case TKIND_TASK:  task = (tcbptr) elem->strucp;
  159.                         st = task->state;
  160.  
  161.                         if (st == ST_WAITING || st == ST_DELAYED)
  162.                            {
  163.                            task->retptr = TTIMEOUT;
  164.                            tsk_wakeup (task);
  165.                            }
  166.                         break;
  167.  
  168.       case TKIND_FLAG:  set_flag ((flagptr) elem->strucp);
  169.                         break;
  170.  
  171.       case TKIND_COUNTER: inc_counter ((counterptr) elem->strucp);
  172.                         break;
  173.  
  174.       case TKIND_PROC:  ((funcptr) elem->strucp)();
  175.                         break;
  176.  
  177.       default:          break;
  178.       }
  179. }
  180.  
  181.  
  182. /*
  183.    The timer task handles all timeouts.
  184.    It maintains a single timer queue, which contains elements of the
  185.    "tlink" structure. No other task is allowed to manipulate this queue,
  186.    except for the insertion of new elements at the queue head. This allows
  187.    stepping through the queue with interrupts enabled.
  188.    CAUTION: This assumes that the operation of loading a far pointer
  189.             is indivisible!
  190. */
  191.  
  192. local void far timer (void)
  193. {
  194.    tlinkptr curr;
  195.    tlinkptr last;
  196.    byte state;
  197.    CRITICAL;
  198.  
  199.    while (1)
  200.       {
  201.       wait_counter_set (&tsk_timer_counter, 0L);
  202.  
  203.       last = (tlinkptr) &tsk_timer;
  204.  
  205.       while ((curr = last->next) != NULL)
  206.          {
  207.          /* Enter critical section for access to state and timeout
  208.             variables. The timer action is also critical.
  209.          */
  210.          C_ENTER;
  211.          if ((state = curr->tstate) >= TSTAT_COUNTDOWN)
  212.             if (!--curr->timeout)
  213.                {
  214.                if (state == TSTAT_COUNTDOWN)
  215.                   state = (byte) TSTAT_REMOVE;
  216.                else
  217.                   curr->timeout = curr->reload;
  218.  
  219.                tsk_timer_action (curr);
  220.                }
  221.          if (state == (byte) TSTAT_REMOVE)
  222.             {
  223.             last->next = curr->next;
  224.             curr->tstate = TSTAT_IDLE;
  225.  
  226. #if (TSK_DYNAMIC)
  227.             if (curr->tkind & TKIND_TEMP)
  228.                {
  229.                if ((curr->tkind & 0x7f) == TKIND_TASK)
  230.                   tsk_kill ((tcbptr) curr->strucp);
  231.                else
  232.                   tsk_free (curr);
  233.                }
  234. #endif
  235.             curr = last;
  236.             }
  237.          C_LEAVE;
  238.          last = curr;
  239.          }
  240.       }
  241. }
  242.  
  243. /*
  244.    int9 is the timer interrupt chain task.
  245. */
  246.  
  247. #if (IBM)
  248.  
  249. local void far int9 (void)
  250. {
  251.    while (1)
  252.       {
  253.       wait_counter_set (&tsk_int9_counter, 0L);
  254.       tsk_chain_timer ();
  255.       }
  256. }
  257.  
  258. #endif
  259.  
  260. /* ---------------------------------------------------------------------- */
  261.  
  262. /*
  263.    create_task
  264.       Initialises a tcb. The task is in stopped state initially.
  265. */
  266.  
  267. tcbptr far create_task (tcbptr task,
  268.                         funcptr func,
  269.                         byteptr stack,
  270.                         word stksz,
  271.                         word prior,
  272.                         farptr arg
  273. #if (TSK_NAMEPAR)
  274.                         ,byteptr name
  275. #endif
  276.                         )
  277.  
  278. {
  279.    struct task_stack far *stk;
  280.  
  281. #if (TSK_DYNAMIC)
  282.    if (task == NULL)
  283.       {
  284.       if ((task = (tcbptr) tsk_alloc (sizeof(tcb))) == NULL)
  285.          return NULL;
  286.       task->flags = F_TEMP;
  287.       }
  288.    else
  289.       task->flags = 0;
  290.  
  291.    if (stack == NULL)
  292.       {
  293.       if ((stack = (byteptr) tsk_alloc (stksz)) == NULL)
  294.          {
  295.          if (task->flags & F_TEMP)
  296.             tsk_free (task);
  297.          return NULL;
  298.          }
  299.       task->flags |= F_STTEMP;
  300.       }
  301. #else
  302.    task->flags = 0;
  303. #endif
  304.  
  305.    stk = (struct task_stack far *)(stack + stksz - sizeof (struct task_stack));
  306.    stk->r_ds = stk->r_es = tsk_dseg ();
  307.    stk->r_bp = 0;
  308.    stk->r_flags = tsk_flags ();
  309.    stk->retn = func;
  310.    stk->dummyret = killretn;
  311.    stk->arg = arg;
  312.  
  313.    task->stkbot = stack;
  314.    task->stack = (byteptr) stk;
  315.    task->next = NULL;
  316.    task->queue = NULL;
  317.    task->state = ST_STOPPED;
  318.    task->prior = task->initprior = prior;
  319.    task->timerq.timeout = task->timerq.reload = 0;
  320.    task->timerq.strucp = (farptr) task;
  321.    task->timerq.tkind = TKIND_TASK;
  322.    task->timerq.tstate = TSTAT_IDLE;
  323.  
  324. #if (TSK_NAMED)
  325.    tsk_add_name (&task->name, name, TYP_TCB, task);
  326. #endif
  327.  
  328.    return task;
  329. }
  330.  
  331.  
  332. /*
  333.    kill_task
  334.       Removes a task from the system.
  335.       CAUTION: The task control block may *not* be immediately re-used
  336.                if it was enqueued in the timer queue. Check for
  337.                task->timerq.tstate == TSTAT_IDLE before modifying
  338.                the tcb.
  339. */
  340.  
  341. void far kill_task (tcbptr task)
  342. {
  343.    byte st;
  344.    CRITICAL;
  345.  
  346.    C_ENTER;
  347.    if ((st = task->state) != ST_RUNNING)
  348.       tsk_unqueue (task);
  349.  
  350.    task->queue = NULL;
  351.    tsk_kill (task);
  352.    if (st == ST_RUNNING)
  353.       {
  354.       tsk_current = NULL;
  355.       schedule ();
  356.       }
  357.    C_LEAVE;
  358. }
  359.  
  360.  
  361. /*
  362.    start_task
  363.       Starts a stopped task. Returns -1 if the task was not stopped.
  364. */
  365.  
  366. int far start_task (tcbptr task)
  367. {
  368.    CRITICAL;
  369.  
  370.    if (task == NULL)
  371.       task = &main_tcb;
  372.  
  373.    if (task->state == ST_STOPPED)
  374.       {
  375.       task->state = ST_ELIGIBLE;
  376.       C_ENTER;
  377.       tsk_enqueue (task, &tsk_eligible);
  378.       C_LEAVE;
  379.       return 0;
  380.       }
  381.    return -1;
  382. }
  383.  
  384.  
  385. /*
  386.    wake_task
  387.       Restarts a task waiting for an event or timeout. 
  388.       Returns -1 if the task was not waiting or stopped.
  389. */
  390.  
  391. int far wake_task (tcbptr task)
  392. {
  393.    CRITICAL;
  394.  
  395.    if (task == NULL)
  396.       task = &main_tcb;
  397.  
  398.    C_ENTER;
  399.    if (task->state >= ST_ELIGIBLE)
  400.       {
  401.       C_LEAVE;
  402.       return -1;
  403.       }
  404.  
  405.    task->retptr = TWAKE;
  406.    tsk_wakeup (task);
  407.    C_LEAVE;
  408.    return 0;
  409. }
  410.  
  411.  
  412.  
  413. /*
  414.    get_priority
  415.       Returns the priority of a task.
  416. */
  417.  
  418. word far get_priority (tcbptr task)
  419. {
  420.    if (task == NULL)
  421.       task = &main_tcb;
  422.  
  423.    return task->prior;
  424. }
  425.  
  426.  
  427. /*
  428.    set_priority
  429.       Changes the priority of a task. If the task is enqueued in a
  430.       queue, its position in the queue is affected.
  431. */
  432.  
  433. void far set_priority (tcbptr task, word prior)
  434. {
  435.    tqueptr que;
  436.    CRITICAL;
  437.  
  438.    if (task == NULL)
  439.       task = &main_tcb;
  440.  
  441.    C_ENTER;
  442.    task->prior = task->initprior = prior;
  443.  
  444.    if ((task->state != ST_RUNNING) && ((que = task->queue) != NULL))
  445.       {
  446.       tsk_unqueue (task);
  447.       tsk_enqueue (task, que);
  448.       }
  449.    C_LEAVE;
  450. }
  451.  
  452. /*
  453.    set_task_flags
  454.       Changes the user modifiable flags of the task.
  455. */
  456.  
  457. void far set_task_flags (tcbptr task, byte flags)
  458. {
  459.    CRITICAL;
  460.  
  461.    if (task == NULL)
  462.       task = &main_tcb;
  463.  
  464.    C_ENTER;
  465.    task->flags = (task->flags & FL_SYSM) | (flags & FL_USRM);
  466.    C_LEAVE;
  467. }
  468.  
  469.  
  470. /* --------------------------------------------------------------------- */
  471.  
  472.  
  473. /*
  474.    t_delay
  475.       delay the current task by "ticks" clock ticks.
  476.       If ticks is zero, the task is stopped.
  477. */
  478.  
  479. int far t_delay (dword ticks)
  480. {
  481.    tsk_cli ();
  482.    tsk_current->queue = NULL;
  483.    if (ticks)
  484.       {
  485.       tsk_current->state = ST_DELAYED;
  486.       tsk_enqtimer (tsk_current, ticks);
  487.       }
  488.    else
  489.       tsk_current->state = ST_STOPPED;
  490.  
  491.    schedule ();
  492.    return (int)tsk_current->retptr;
  493. }
  494.  
  495.  
  496. /* --------------------------------------------------------------------- */
  497.  
  498.  
  499. /*
  500.    install_tasker
  501.       Installs the Ctask system. The internal tasks are created,
  502.       the queues are initialised, and the interrupt handler installation
  503.       routines are called. Task preemption is initially off.
  504.  
  505.       Handling of the speedup parameter is system dependent.
  506. */
  507.  
  508. void far install_tasker (byte varpri, int speedup)
  509. {
  510.    word divisor, sys_ticks;
  511.  
  512.    tsk_current = &main_tcb;
  513.    tsk_eligible = NULL;
  514.    tsk_timer = NULL;
  515.    tsk_preempt = 1;
  516.    tsk_pretick = 0;
  517.    tsk_var_prior = varpri;
  518.  
  519. #if (TSK_NAMED)
  520.    tsk_name_list.follow = tsk_name_list.prev = &tsk_name_list;
  521.    tsk_name_list.nkind = 0;
  522.    tsk_name_list.name [0] = 0;
  523. #endif
  524.  
  525.    create_task (&timer_tcb, timer, timer_stack, STACKSIZE, PRI_TIMER, NULL
  526. #if (TSK_NAMEPAR)
  527.                 , "-TIMER-"
  528. #endif
  529.                 );
  530.    create_counter (&tsk_timer_counter
  531. #if (TSK_NAMEPAR)
  532.                 , "TIMCOUNT"
  533. #endif
  534.                 );
  535.  
  536. #if (TSK_DYNAMIC)
  537.    create_resource (&alloc_resource
  538. #if (TSK_NAMEPAR)
  539.                 , "ALLOCRSC"
  540. #endif
  541.                 );
  542. #endif
  543.  
  544.    main_tcb.prior = main_tcb.initprior = PRI_TIMER - 1;
  545.    main_tcb.queue = &tsk_eligible;
  546.    main_tcb.flags = F_CRIT;
  547.    main_tcb.state = ST_RUNNING;
  548.    start_task (&timer_tcb);
  549.  
  550. #if (IBM)
  551.    create_task (&int9_tcb, int9, int9_stack, STACKSIZE, PRI_INT9, NULL
  552. #if (TSK_NAMEPAR)
  553.                 , "-INT9-"
  554. #endif
  555.                 );
  556.    create_counter (&tsk_int9_counter
  557. #if (TSK_NAMEPAR)
  558.                 , "INT9CNT"
  559. #endif
  560.                 );
  561.    start_task (&int9_tcb);
  562.  
  563.    if (speedup <= 0 || speedup > 8)
  564.       {
  565.       divisor = 0;
  566.       sys_ticks = 1;
  567.       }
  568.    else
  569.       {
  570.       divisor = 0x8000 >> (speedup - 1);
  571.       sys_ticks = 1 << speedup;
  572.       }
  573.  
  574.    ticks_per_sec = 18 * sys_ticks;  /* rough number only */
  575.  
  576. #if (CLOCK_MSEC)
  577.    tick_factor = (65536.0 / (double)sys_ticks) / 1193.18;
  578. #endif
  579.  
  580.    tsk_install_timer (divisor, sys_ticks);
  581.    tsk_install_kbd ();
  582. #endif
  583. #if (AT_BIOS)
  584.    tsk_install_bios ();
  585. #endif
  586.  
  587. #if (DOS)
  588.    tsk_install_dos ();
  589. #endif
  590. }
  591.  
  592.  
  593. /*
  594.    preempt_off
  595.       Turns off task preemption (will stay off until explicitly enabled).
  596. */
  597.  
  598. void far preempt_off (void)
  599. {
  600.    tsk_preempt = 1;
  601. }
  602.  
  603.  
  604. /*
  605.    preempt_on
  606.       Resets permanent and temporary task preemption flag. If 
  607.       preemption is pending, the scheduler is called.
  608. */
  609.  
  610. void far preempt_on (void)
  611. {
  612.    tsk_preempt = 0;
  613.    tsk_cli ();
  614.    if (tsk_pretick)
  615.       schedule ();
  616.    tsk_sti ();
  617. }
  618.  
  619.  
  620. /*
  621.    remove_tasker
  622.       Calls the interrupt handler un-install routines.
  623. */
  624.  
  625. void far remove_tasker (void)
  626. {
  627.    tsk_preempt = 0;
  628.  
  629. #if (AT_BIOS)
  630.    tsk_remove_bios ();
  631. #endif
  632. #if (IBM)
  633.  
  634.    if (v24_remove_func != NULL)
  635.       v24_remove_func ();
  636.    if (prt_remove_func != NULL)
  637.       prt_remove_func ();
  638.  
  639.    /* Allow all stored clock ticks to be processed */
  640.    set_priority (&int9_tcb, 0xffff);
  641.    while (check_counter (&tsk_int9_counter))
  642.       schedule();
  643.  
  644.    tsk_remove_timer ();
  645.    tsk_remove_kbd ();
  646. #endif
  647.  
  648. #if (DOS)
  649.    tsk_remove_dos ();
  650. #endif
  651. }
  652.  
  653.  
  654. /*
  655.    tsk_ena_preempt
  656.       Resets temporary task preemption flag. If preemption is pending,
  657.       the scheduler is called.
  658. */
  659.  
  660. void far tsk_ena_preempt (void)
  661. {
  662.    tsk_cli ();
  663.    if (!(tsk_preempt &= ~2))
  664.       if (tsk_pretick)
  665.          schedule ();
  666.    tsk_sti ();
  667. }
  668.  
  669.  
  670. /* --------------------------------------------------------------------- */
  671.  
  672. /*
  673.    create_timer
  674.       Creates a timer queue element. The element is inserted into
  675.       the timeout queue.
  676. */
  677.  
  678. tlinkptr far create_timer (tlinkptr elem, dword tout, farptr strucp,
  679.                            byte kind, byte rept)
  680. {
  681.    CRITICAL;
  682.  
  683.    if (kind <= TKIND_TASK || kind > TKIND_COUNTER)
  684.       return NULL;
  685.  
  686. #if (TSK_DYNAMIC)
  687.  
  688.    if (elem == NULL)
  689.       {
  690.       if ((elem = tsk_alloc (sizeof (tlink))) == NULL)
  691.          return NULL;
  692.       kind |= TKIND_TEMP;
  693.       }
  694.  
  695. #endif
  696.  
  697.    elem->tkind = kind;
  698.    elem->strucp = strucp;
  699.    elem->tstate = (byte)((rept) ? TSTAT_REPEAT : TSTAT_COUNTDOWN);
  700.    elem->timeout = elem->reload = tsk_timeout(tout);
  701.  
  702.    C_ENTER;
  703.    elem->next = tsk_timer;
  704.    tsk_timer = elem;
  705.    C_LEAVE;
  706.  
  707.    return elem;
  708. }
  709.  
  710.  
  711. /*
  712.    delete_timer
  713.       Deletes a timeout element.
  714. */
  715.  
  716. void far delete_timer (tlinkptr elem)
  717. {
  718.    CRITICAL;
  719.  
  720.    C_ENTER;
  721.    if (elem->tstate != TSTAT_IDLE)
  722.       {
  723.       elem->tstate = (byte)TSTAT_REMOVE;
  724.       C_LEAVE;
  725.       return;
  726.       }
  727.    C_LEAVE;
  728.  
  729. #if (TSK_DYNAMIC)
  730.    if (elem->tkind & TKIND_TEMP)
  731.       tsk_free (elem);
  732. #endif
  733. }
  734.  
  735.  
  736. /*
  737.    change_timer
  738.       Changes the timeout and/or repeat-flag in a timer element.
  739.       If the timer was idle, it is inserted into the timeout queue.
  740.  
  741.       If 0 is passed as timeout, the element is removed from the
  742.       timeout queue (same as delete_timer).
  743.  
  744.       This routine should *not* be used for dynamically allocated
  745.       timer elements.
  746. */
  747.  
  748. void far change_timer (tlinkptr elem, dword tout, byte rept)
  749. {
  750.    byte st;
  751.    CRITICAL;
  752.  
  753.    if (!tout)
  754.       {
  755.       delete_timer (elem);
  756.       return;
  757.       }
  758.  
  759.    C_ENTER;
  760.    elem->timeout = elem->reload = tsk_timeout(tout);
  761.    st = elem->tstate;
  762.    elem->tstate = (byte)((rept) ? TSTAT_REPEAT : TSTAT_COUNTDOWN);
  763.  
  764.    if (st == TSTAT_IDLE)
  765.       {
  766.       elem->next = tsk_timer;
  767.       tsk_timer = elem;
  768.       }
  769.    C_LEAVE;
  770. }
  771.  
  772.